/**
 * @file liquidity_taker.h
 * @brief 流动性吸纳策略类的定义
 * 
 * @details 该文件定义了流动性吸纳策略类，该策略基于市场交易信息和特征引擎计算的特征，
 * 在特定条件下主动发送订单以吸纳市场流动性。该策略主要关注主动交易数量比率，
 * 当该比率超过阈值时，会发送主动订单。
 * 
 * @author 原作者
 * @date 2023
 */

#pragma once

#include "common/macros.h"
#include "common/logging.h"

#include "order_manager.h"
#include "feature_engine.h"

using namespace Common;

namespace Trading {
  /**
   * @brief 流动性吸纳策略类
   * 
   * @details 该类实现了一种交易策略，通过监控市场交易活动并利用特征引擎计算的主动交易数量比率，
   * 在特定条件下主动发送订单以吸纳市场流动性。当主动交易数量比率超过配置的阈值时，
   * 策略会发送相应的主动订单。该策略主要关注市场交易事件，而不对订单簿更新做出反应。
   */
  class LiquidityTaker {
  public:
    /**
     * @brief 构造函数
     * 
     * @details 初始化流动性吸纳策略对象，设置必要的组件和配置，并将策略的回调函数注册到交易引擎中。
     * 
     * @param logger 日志记录器指针，用于记录策略执行过程和结果
     * @param trade_engine 交易引擎指针，用于注册策略的回调函数
     * @param feature_engine 特征引擎指针，用于获取策略所需的特征值
     * @param order_manager 订单管理器指针，用于发送和管理策略的订单
     * @param ticker_cfg 交易配置映射，包含每个证券的交易参数（如阈值、批量等）
     */
    LiquidityTaker(Common::Logger *logger, TradeEngine *trade_engine, const FeatureEngine *feature_engine,
                   OrderManager *order_manager,
                   const TradeEngineCfgHashMap &ticker_cfg);

    /**
     * @brief 处理订单簿更新
     * 
     * @details 对于流动性吸纳策略，该方法仅记录订单簿更新信息，不执行实际的交易操作。
     * 该策略主要基于交易事件做出决策，而不是订单簿变化。
     * 
     * @param ticker_id 证券标识符
     * @param price 变化的价格
     * @param side 变化的买卖方向
     * @param book 市场订单簿指针（本方法未使用）
     * 
     * @note 该方法不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto onOrderBookUpdate(TickerId ticker_id, Price price, Side side, MarketOrderBook *) noexcept -> void {
      logger_->log("%:% %() % ticker:% price:% side:%\n", __FILE__, __LINE__, __FUNCTION__,
                   Common::getCurrentTimeStr(&time_str_), ticker_id, Common::priceToString(price).c_str(),
                   Common::sideToString(side).c_str());
    }

    /**
     * @brief 处理交易事件
     * 
     * @details 该方法是流动性吸纳策略的核心逻辑，它处理市场交易事件，获取特征引擎计算的主动交易数量比率，
     * 并将其与交易阈值进行比较。当主动交易数量比率超过阈值时，策略会发送主动订单。
     * 具体流程如下：
     * 1. 获取当前盘口状态和主动交易数量比率
     * 2. 验证盘口价格和主动交易数量比率的有效性
     * 3. 从配置中获取相应证券的批量和阈值
     * 4. 如果主动交易数量比率超过阈值，则发送相应的主动订单
     * 
     * @param market_update 市场更新指针，包含交易信息
     * @param book 市场订单簿指针，包含当前的盘口状态
     * 
     * @note 该方法不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto onTradeUpdate(const Exchange::MEMarketUpdate *market_update, MarketOrderBook *book) noexcept -> void {
      logger_->log("%:% %() % %\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str_),
                   market_update->toString().c_str());

      const auto bbo = book->getBBO();
      const auto agg_qty_ratio = feature_engine_->getAggTradeQtyRatio();

      if (LIKELY(bbo->bid_price_ != Price_INVALID && bbo->ask_price_ != Price_INVALID && agg_qty_ratio != Feature_INVALID)) {
        logger_->log("%:% %() % % agg-qty-ratio:%\n", __FILE__, __LINE__, __FUNCTION__,
                     Common::getCurrentTimeStr(&time_str_),
                     bbo->toString().c_str(), agg_qty_ratio);

        const auto clip = ticker_cfg_.at(market_update->ticker_id_).clip_;
        const auto threshold = ticker_cfg_.at(market_update->ticker_id_).threshold_;

        if (agg_qty_ratio >= threshold) {
          START_MEASURE(Trading_OrderManager_moveOrders);
          if (market_update->side_ == Side::BUY)
            order_manager_->moveOrders(market_update->ticker_id_, bbo->ask_price_, Price_INVALID, clip);
          else
            order_manager_->moveOrders(market_update->ticker_id_, Price_INVALID, bbo->bid_price_, clip);
          END_MEASURE(Trading_OrderManager_moveOrders, (*logger_));
        }
      }
    }

    /**
     * @brief 处理订单更新
     * 
     * @details 处理策略发送的订单的客户端响应。该方法记录订单响应信息，
     * 并将其转发给订单管理器进行处理。这样可以确保订单管理器维护正确的订单状态。
     * 
     * @param client_response 客户端响应指针，包含订单状态更新信息
     * 
     * @note 该方法不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto onOrderUpdate(const Exchange::MEClientResponse *client_response) noexcept -> void {
      logger_->log("%:% %() % %\n", __FILE__, __LINE__, __FUNCTION__, Common::getCurrentTimeStr(&time_str_),
                   client_response->toString().c_str());
      START_MEASURE(Trading_OrderManager_onOrderUpdate);
      order_manager_->onOrderUpdate(client_response);
      END_MEASURE(Trading_OrderManager_onOrderUpdate, (*logger_));
    }

    /**
     * @brief 已删除的构造函数和赋值运算符
     * 
     * @details 为了防止意外复制或移动流动性吸纳策略对象，禁用了默认构造函数、复制构造函数、
     * 移动构造函数以及复制赋值和移动赋值运算符。
     * 流动性吸纳策略对象应该始终使用显式的参数初始化。
     */
    LiquidityTaker() = delete;

    LiquidityTaker(const LiquidityTaker &) = delete;

    LiquidityTaker(const LiquidityTaker &&) = delete;

    LiquidityTaker &operator=(const LiquidityTaker &) = delete;

    LiquidityTaker &operator=(const LiquidityTaker &&) = delete;

  private:
    /**
     * @brief 特征引擎指针
     * 
     * @details 驱动流动性吸纳策略的特征引擎指针，用于获取主动交易数量比率等特征值。
     * 该指针不拥有特征引擎对象的所有权，因此声明为 const。
     */
    const FeatureEngine *feature_engine_ = nullptr;

    /**
     * @brief 订单管理器指针
     * 
     * @details 用于发送和管理流动性吸纳策略的主动订单的订单管理器指针。
     * 该策略通过调用 moveOrders 方法来发送主动订单。
     */
    OrderManager *order_manager_ = nullptr;

    /**
     * @brief 时间字符串缓存
     * 
     * @details 用于日志记录的时间字符串缓存，避免重复创建时间字符串
     */
    std::string time_str_;
    
    /**
     * @brief 日志记录器指针
     * 
     * @details 用于记录策略执行过程和结果的日志记录器指针
     */
    Common::Logger *logger_ = nullptr;

    /**
     * @brief 交易配置映射
     * 
     * @details 保存流动性吸纳策略的交易配置，包含每个证券的交易参数，
     * 如阈值（threshold_）和批量（clip_）。该映射在构造函数中初始化，且不可修改，
     * 因此声明为 const。
     */
    const TradeEngineCfgHashMap ticker_cfg_;
  };
}
